import com.gwc.intervaltree.DateInterval;
import com.gwc.intervaltree.Interval;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class Schedule {
    private Data data;
    HashMap<String, ArrayList<String[]>> scheduledTask_arcs;//任务-已调度弧段集合
    public final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//日期时间格式

    public Schedule() throws ParseException {
        data = Data.getData();
        this.scheduledTask_arcs = new HashMap<>();
    }

    //二分法插入时间节点（有序）
    public int binaryInsertTime(ArrayList<String[]> arcs, String id, String time, String mark, String devMode) throws ParseException {
        Date time_ = df.parse(time);
        int left = 0;
        int right = arcs.size() - 1;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (time_.after(df.parse(arcs.get(mid)[1]))) {
                left = mid + 1;
            } else if (time_.before(df.parse(arcs.get(mid)[1]))) {
                right = mid - 1;
            } else {
                arcs.add(mid, new String[]{id, time, mark, devMode});
                return mid;
            }
        }
        arcs.add(left, new String[]{id, time, mark, devMode});
        return left;
    }

    //计算最大重叠区间个数
    public int countMaxOverlap(ArrayList<String[]> arcs) {
        // arcs:[id timeStamp tag devMode]
        int count = 0;
        int max = Integer.MIN_VALUE;
        for (String[] arc : arcs) {
            count += Integer.parseInt(arc[2]);
            if (count > max) max = count;
        }
        return max;
    }

    //计算时间差（秒）
    public long calculateTime(String startTime, String endTime) throws ParseException {
        Date d1 = df.parse(startTime);
        Date d2 = df.parse(endTime);
        long diff = d2.getTime() - d1.getTime();
        return diff / 1000;
    }

    //选择调度某一弧段
    public void insertArc(String[] arc, Require require) throws ParseException {
        this.scheduledTask_arcs.get(arc[1]).add(arc);//分配弧段,加入调度集合
        data.scheduledArcs.add(arc[0]);//分配弧段,加入调度集合
        binaryInsertTime(data.dev_timeAxis.get(arc[2]), arc[0], arc[5], "-1", require.devMode);// 插入结束时间节点
        binaryInsertTime(data.dev_timeAxis.get(arc[2]), arc[0], arc[3], "1", require.devMode);// 插入开始时间节点
        binaryInsertTime(data.sat_timeAxis.get(arc[1]), arc[0], arc[5], "-1", require.devMode);// 插入结束时间节点
        binaryInsertTime(data.sat_timeAxis.get(arc[1]), arc[0], arc[3], "1", require.devMode);// 插入开始时间节点
        data.dev_mode_intervalTree.get(arc[2]).get(require.devMode).add(new DateInterval(df.parse(arc[3]), df.parse(arc[5]), Interval.Bounded.CLOSED, arc[0]));//区间树插入该弧段
    }

    //回收某一弧段
    public void withdrawArc(String[] arc, Require require) throws ParseException {
        data.scheduledArcs.remove(arc[0]);//调度集合中移除该弧段
        data.dev_timeAxis.get(arc[2]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
        data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));
        data.dev_mode_intervalTree.get(arc[2]).get(require.devMode).remove(new DateInterval(df.parse(arc[3]), df.parse(arc[5]), Interval.Bounded.CLOSED, arc[0]));//区间树移除该弧段
    }

    //全弧段跟踪
    public void followAllArc(Require require) throws ParseException {
        for (String[] task : require.subRequireData) { //遍历任务
            if (data.sat_arcs.containsKey(task[1])) { //该任务目标有对应的弧段
                for (String[] arc : data.sat_arcs.get(task[1])) { //遍历弧段
                    //若弧段已经被调度过，则不可用，跳过
                    if (data.scheduledArcs.contains(arc[0])) {
                        continue;
                    }
                    if (!data.dev_timeAxis.containsKey(arc[2])) {
                        data.dev_timeAxis.put(arc[2], new ArrayList<>());
                    }
                    if (!data.sat_timeAxis.containsKey(arc[1])) {
                        data.sat_timeAxis.put(arc[1], new ArrayList<>());
                    }
                    //全弧段跟踪：对于每一目标下的每一弧段，只要不冲突就将其分配
                    if (require.devGroup.contains(arc[2]) && conflictCheck(arc, require.devMode) && arcIntervalCheck(arc, require) && switchTimeCheck(arc, require)) {
                        if (!this.scheduledTask_arcs.containsKey(task[1])) {
                            this.scheduledTask_arcs.put(task[1], new ArrayList<>());
                        }
                        insertArc(arc, require);
                    }
                }
            }
        }
    }

    //同圈跟踪站数
    public void singleTask(Require require) throws ParseException {
        for (String[] task : require.subRequireData) { //遍历任务
            // 对每个子任务开始调度
            if (data.sat_arcs.containsKey(task[1])) { //该任务目标有对应的弧段
                // 放入调度任务结果
                this.scheduledTask_arcs.put(task[1], new ArrayList<>());
                // 扫描三级索引
                HashMap<String, HashMap<String, ArrayList<String[]>>> circle_dev_arcs = data.sat_circle_dev_arcs.get(task[1]);
                // 查看每个圈次
                for (HashMap<String, ArrayList<String[]>> dev_arcs : circle_dev_arcs.values()) {
                    // 该圈次装备数少于需求，略过
                    if (dev_arcs.size() < require.singleTask) continue;
                    // 每个圈次处理调度
                    int dev_cot = 0;
                    // 每个装备对应的arcs选择一个arc
                    for (ArrayList<String[]> arcs : dev_arcs.values()) {
                        for (String[] arc : arcs) {
                            //若弧段已经被调度过，则不可用，跳过
                            if (data.scheduledArcs.contains(arc[0])) {
                                continue;
                            }
                            if (!data.dev_timeAxis.containsKey(arc[2])) {
                                data.dev_timeAxis.put(arc[2], new ArrayList<>());
                            }
                            if (!data.sat_timeAxis.containsKey(arc[1])) {
                                data.sat_timeAxis.put(arc[1], new ArrayList<>());
                            }
                            int workTime = Integer.parseInt(arc[10]);
                            double startElevationDeg = Double.parseDouble(arc[4]);
                            double endPElevationDeg = Double.parseDouble(arc[6]);
                            double topPElevationDeg = Double.parseDouble(arc[7]);
                            //根据装备组、时长、仰角等约束筛选弧段
                            if (require.devGroup.contains(arc[2]) && workTime >= require.minWorkTime && workTime <= require.maxWorkTime && topPElevationDeg <= require.maxTopel && startElevationDeg >= require.minTopel && endPElevationDeg >= require.minTopel && topPElevationDeg >= require.minTopel &&
                                    conflictCheck(arc, require.devMode) && arcIntervalCheck(arc, require) && switchTimeCheck(arc, require)) {
                                insertArc(arc, require);
                                dev_cot++;
                                break;
                            }
                        }
                        if (dev_cot >= require.singleTask) break;
                    }
                    if (this.scheduledTask_arcs.get(task[1]).size() < require.singleTask) {
                        // 回收弧段
                        for (String[] arc : this.scheduledTask_arcs.get(task[1])) {
                            withdrawArc(arc, require);
                        }
                        this.scheduledTask_arcs.remove(task[1]);//调度集合中移除该任务
                    }
                }
            }
        }
    }

    //升/降轨调度
    public void riseAndFall(Require require) throws ParseException {
        for (String[] task : require.subRequireData) { //遍历任务
            int rise_scheduled = 0;//记录已分配的升轨圈数
            int fall_scheduled = 0;//记录已分配的降轨圈数
            if (data.sat_arcs.containsKey(task[1])) { //该任务目标有对应的弧段
                this.scheduledTask_arcs.put(task[1], new ArrayList<>());
                for (String[] arc : data.sat_arcs.get(task[1])) {
                    //若弧段已经被调度过，则不可用，跳过
                    if (data.scheduledArcs.contains(arc[0])) {
                        continue;
                    }
                    //若该任务已经满足，跳过，继续执行下一个任务
                    if (rise_scheduled == require.riseCount && fall_scheduled == require.fallCount) {
                        break;
                    }
                    if (!data.dev_timeAxis.containsKey(arc[2])) {
                        data.dev_timeAxis.put(arc[2], new ArrayList<>());
                    }
                    if (!data.sat_timeAxis.containsKey(arc[1])) {
                        data.sat_timeAxis.put(arc[1], new ArrayList<>());
                    }
                    //根据装备组、时长、仰角等约束筛选弧段
                    int workTime = Integer.parseInt(arc[10]);
                    double startElevationDeg = Double.parseDouble(arc[4]);
                    double endPElevationDeg = Double.parseDouble(arc[6]);
                    double topPElevationDeg = Double.parseDouble(arc[7]);
                    if (require.devGroup.contains(arc[2]) && workTime >= require.minWorkTime && workTime <= require.maxWorkTime && topPElevationDeg <= require.maxTopel && startElevationDeg >= require.minTopel && endPElevationDeg >= require.minTopel && topPElevationDeg >= require.minTopel && conflictCheck(arc, require.devMode) && arcIntervalCheck(arc, require) && switchTimeCheck(arc, require)) {
                        //可分配的升轨弧段
                        if (arc[8].equals("RISE") && rise_scheduled < require.riseCount) {
                            rise_scheduled++;
                            insertArc(arc, require);
                        } else if (arc[8].equals("FALL") && fall_scheduled < require.fallCount) { //可分配的降轨弧段
                            fall_scheduled++;
                            insertArc(arc, require);
                        }
                    }
                }
                //若任务未能满足，回收弧段
                if (this.scheduledTask_arcs.get(task[1]).size() < require.riseCount + require.fallCount) {
                    for (String[] arc : this.scheduledTask_arcs.get(task[1])) {
                        withdrawArc(arc, require);
                    }
                    this.scheduledTask_arcs.remove(task[1]);//调度集合中移除该任务
                }
            }
        }
    }

    //总圈次数调度
    public void totalCircleCount(Require require) throws ParseException {
        for (String[] task : require.subRequireData) { //遍历任务
            int circleCount_scheduled = 0;//记录已分配的圈数
            if (data.sat_arcs.containsKey(task[1])) { //该任务目标有对应的弧段
                this.scheduledTask_arcs.put(task[1], new ArrayList<>());
                for (String[] arc : data.sat_arcs.get(task[1])) {
                    //若弧段已经被调度过，则不可用，跳过
                    if (data.scheduledArcs.contains(arc[0])) {
                        continue;
                    }
                    //若该任务已经满足，跳过，继续执行下一个任务
                    if (circleCount_scheduled == require.circleCount) {
                        break;
                    }
                    if (!data.dev_timeAxis.containsKey(arc[2])) {
                        data.dev_timeAxis.put(arc[2], new ArrayList<>());
                    }
                    if (!data.sat_timeAxis.containsKey(arc[1])) {
                        data.sat_timeAxis.put(arc[1], new ArrayList<>());
                    }
                    //根据装备组、时长、仰角等约束筛选弧段
                    int workTime = Integer.parseInt(arc[10]);
                    double startElevationDeg = Double.parseDouble(arc[4]);
                    double endPElevationDeg = Double.parseDouble(arc[6]);
                    double topPElevationDeg = Double.parseDouble(arc[7]);
                    //可分配的弧段
                    if (require.devGroup.contains(arc[2]) && workTime >= require.minWorkTime && workTime <= require.maxWorkTime && topPElevationDeg <= require.maxTopel && startElevationDeg >= require.minTopel && endPElevationDeg >= require.minTopel && topPElevationDeg >= require.minTopel && conflictCheck(arc, require.devMode) && arcIntervalCheck(arc, require) && switchTimeCheck(arc, require)) {
                        circleCount_scheduled++;
                        insertArc(arc, require);
                    }
                }
                //若任务未能满足，回收弧段
                if (this.scheduledTask_arcs.get(task[1]).size() < require.circleCount) {
                    for (String[] arc : this.scheduledTask_arcs.get(task[1])) {
                        withdrawArc(arc, require);
                    }
                    this.scheduledTask_arcs.remove(task[1]);//调度集合中移除该任务
                }
            }
        }
    }

    //冲突判断(true表示满足条件不冲突，false表示冲突）
    public boolean conflictCheck(String[] arc, String devMode) throws ParseException {
        binaryInsertTime(data.dev_timeAxis.get(arc[2]), arc[0], arc[5], "-1", devMode);// 插入结束时间节点
        binaryInsertTime(data.dev_timeAxis.get(arc[2]), arc[0], arc[3], "1", devMode);// 插入开始时间节点
        if (countMaxOverlap(data.dev_timeAxis.get(arc[2])) > Integer.parseInt(data.devData.get(arc[2])[2])) {
            //重叠数超过装备的多目标能力，冲突，该弧段不可用
            data.dev_timeAxis.get(arc[2]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
            return false;
        }
        data.dev_timeAxis.get(arc[2]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
        return true;
    }

    //弧段间隔判断（目前认为对于同一个目标，弧段不可重叠）
    public boolean arcIntervalCheck(String[] arc, Require require) throws ParseException {
        int rightIndex = binaryInsertTime(data.sat_timeAxis.get(arc[1]), arc[0], arc[5], "-1", require.devMode);// 插入结束时间节点
        int leftIndex = binaryInsertTime(data.sat_timeAxis.get(arc[1]), arc[0], arc[3], "1", require.devMode);// 插入开始时间节点
        if (data.sat_timeAxis.get(arc[1]).size() <= 2) { //若只有一个弧段，可以插入
            data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
            return true;
        }
        if (countMaxOverlap(data.sat_timeAxis.get(arc[1])) > 1) { //若有重叠，不能插入
            data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
            return false;
        }
        //若不满足弧段间隔要求，则不可用
        if (leftIndex == 0) { //特殊情况1：插入的弧段时间窗口是最左边的
            //计算区间间隔
            long interval = calculateTime(data.sat_timeAxis.get(arc[1]).get(rightIndex)[1], data.sat_timeAxis.get(arc[1]).get(rightIndex + 1)[1]);
            if (interval > require.maxArcInterval || interval < require.minArcInterval) {
                data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
                return false;
            } else {
                data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
                return true;
            }
        }
        if (rightIndex == data.sat_timeAxis.get(arc[1]).size() - 1) { //特殊情况2：插入的弧段时间窗口是最右边的
            //计算区间间隔
            long interval = calculateTime(data.sat_timeAxis.get(arc[1]).get(leftIndex - 1)[1], data.sat_timeAxis.get(arc[1]).get(leftIndex)[1]);
            if (interval > require.maxArcInterval || interval < require.minArcInterval) {
                data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
                return false;
            } else {
                data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
                return true;
            }
        }
        long interval1 = calculateTime(data.sat_timeAxis.get(arc[1]).get(leftIndex - 1)[1], data.sat_timeAxis.get(arc[1]).get(leftIndex)[1]);//插入的弧段与左边时间窗口的间隔
        long interval2 = calculateTime(data.sat_timeAxis.get(arc[1]).get(rightIndex)[1], data.sat_timeAxis.get(arc[1]).get(rightIndex + 1)[1]);//插入的弧段与右边时间窗口的间隔
        if (interval1 > require.maxArcInterval || interval1 < require.minArcInterval || interval2 > require.maxArcInterval || interval2 < require.minArcInterval) {
            data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
            return false;
        }
        data.sat_timeAxis.get(arc[1]).removeIf(tw -> Objects.equals(tw[0], arc[0]));//时间轴上移除该弧段的时间节点
        return true;
    }

    //装备工作模式切换时间判断
    public boolean switchTimeCheck(String[] arc, Require require) throws ParseException {
        String dev = arc[2];
        String devMode = require.devMode;
        int switchTime = Integer.parseInt(data.devData.get(dev)[9]);
        Date originStartTime = df.parse(arc[3]);//该弧段的原始起始时间
        Date originEndTime = df.parse(arc[5]);//该弧段的原始结束时间
        Calendar c = Calendar.getInstance();
        c.setTime(originStartTime);
        c.add(Calendar.SECOND, 0 - switchTime);
        Date newStartTime = c.getTime();//时间窗口扩充，新起始时间=原始起始时间-装备工作模式切换时间
        c.setTime(originEndTime);
        c.add(Calendar.SECOND, switchTime);
        Date newEndTime = c.getTime();//时间窗口扩充，新结束时间=原始结束时间+装备工作模式切换时间
        for (String mode : data.dev_mode_intervalTree.get(dev).keySet()) {
            if (!devMode.equals(mode)) { //扩充后的时间窗口输入到不同模式下的区间树进行判断
                //若有重叠，则说明模式切换时间上有冲突，不可行
                if (data.dev_mode_intervalTree.get(dev).get(mode).query(new DateInterval(newStartTime, newEndTime, Interval.Bounded.CLOSED, arc[0])).size() != 0) {
                    return false;
                }
            }
        }
        return true;
    }

    //对需求，进行任务调度
    public void scheduleRequire(Require require) throws ParseException {
        System.out.println("开始调度需求" + require.requireID + "：" + df.format(new Date()));
        //判断是否全弧段跟踪
        if (require.followAllArc == 1) {
            followAllArc(require);
        }
        //同圈跟踪站数
        if (require.singleTask != 0) {
            singleTask(require);
        }
        //升/降轨或同圈次
        if (require.riseCount != 0 || require.fallCount != 0) {
            riseAndFall(require);
        } else if (require.circleCount != 0 && require.riseCount == 0 && require.fallCount == 0) {
            totalCircleCount(require);
        }
        System.out.println("调度需求" + require.requireID + "完成：" + df.format(new Date()));
        System.out.println(this.scheduledTask_arcs.size());
        this.scheduledTask_arcs.clear();
    }

    public static void main(String[] args) throws ParseException {
        Schedule schedule = new Schedule();
        Require require = new Require();
        require.getRequire("499");
        require.verifyRequire();
        schedule.scheduleRequire(require);
        Require require2 = new Require();
        require2.getRequire("497");
        require2.verifyRequire();
        schedule.scheduleRequire(require2);
    }
}
